package com.xzzz.irda.auth.core.filter.impl;


import cn.hutool.core.util.StrUtil;
import com.xzzz.irda.auth.core.authorization.AbstractAuthorization;
import com.xzzz.irda.auth.core.authorization.AuthContext;
import com.xzzz.irda.auth.core.authorization.Authorization;
import com.xzzz.irda.auth.core.AuthBaseConfiguration;
import com.xzzz.irda.auth.core.constants.AuthConstant;
import com.xzzz.irda.auth.core.exception.AuthException;
import com.xzzz.irda.auth.core.exception.AuthRCode;
import com.xzzz.irda.auth.core.filter.AuthBaseFilterConfiguration;
import com.xzzz.irda.auth.core.filter.AuthFilter;
import com.xzzz.irda.auth.core.filter.AuthFilterChain;
import com.xzzz.irda.auth.core.AuthBaseProperties;
import com.xzzz.irda.auth.core.token.AccessToken;
import com.xzzz.irda.auth.core.util.TokenUtil;
import com.xzzz.common.base.util.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * [100]
 *
 * 请求验证, 校验请求是否携带 token, 未携带的进行拦截跑出错误
 *
 * 注入方式:
 * 由 {@link AuthBaseFilterConfiguration} 注入
 *
 * @author wangyf
 *
 */
@Slf4j
public class AuthorizationValidateFilter implements AuthFilter {

    /**
     * 配置文件内容
     */
    @Autowired
    private AuthBaseProperties properties;

    /**
     * 公用基础配置
     */
    @Autowired
    private AuthBaseConfiguration authBaseConfiguration;

    /**
     * 执行过滤器
     * @param request request
     * @param response response
     * @param authFilterChain 过滤器链
     * @throws IOException io
     * @throws ServletException servlet
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, AuthFilterChain authFilterChain) throws IOException, ServletException {
        log.debug("[AUTHORIZ] filter({}) 授权校验过滤器", getOrder());

        boolean isWhiteList = Boolean.TRUE.equals(request.getAttribute(AuthConstant.WHITE_LIST_ATTRIBUTE_KEY));
        // 解析请求头中的 token
        String token = getHeaderToken(request);

        if (isWhiteList && StrUtil.isBlank(token)) {
            return;
        }

        // 根据 token 取出 Redis 中的授权数据
        Authorization authorization = authBaseConfiguration.getAuthorizationRepository().getAuthorization(token);

        // 判断授权信息
        if (null == authorization){
            // 如果是白名单请求没有授权信息,则构造空的授权信息到上下文,异常由调用方捕获
            if (isWhiteList) {
                Authorization authorizationNull = new AbstractAuthorization();
                authorizationNull.setAccessToken(new AccessToken());
                setAuthContext(authorizationNull);
                return;
            }
            // 如果不是白名单请求,则抛出错误
            throw new AuthException(AuthRCode.INVALID_TOKEN);
        }

        // 如果白名单请求,且获得了token,则说明是登录状态请求白名单的接口,正常执行下一过滤器
        if (isWhiteList) {
            // 5. 将授权数据存放到上下文中,不执行后续过滤器
            setAuthContext(authorization);
            return;
        }
        // 5 将授权数据存放到上下文中,并执行下一过滤器
        setAuthContext(authorization);
        authFilterChain.doFilter(request,response);
    }

    /**
     * 1. 从请求头中获取 Token
     * @param servletRequest request
     * @return Token
     */
    private String getHeaderToken (ServletRequest servletRequest) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        /*
        请求头中无法获取到 token 时,尝试从 cookie 中获取, 采用 cookie 跨域单点登录的解决方案较为简单
         */
        String token = httpServletRequest.getHeader(AuthConstant.HEADER_AUTHORIZATION);
        if (StrUtil.isBlank(token)) {
            token = httpServletRequest.getHeader(AuthConstant.HEADER_AUTHORIZATION.toLowerCase());
        }

        if (StrUtil.isBlank(token)) {
            token = httpServletRequest.getHeader(AuthConstant.HEADER_AUTHORIZATION.toUpperCase());
        }

        if (StrUtil.isBlank(token)) {
            Cookie cookie = ServletUtil.getCookie(httpServletRequest, AuthConstant.HEADER_AUTHORIZATION);
            if (cookie != null) {
                token = cookie.getValue();
            }
        }

        // 以 Bearer 开头 && 除此外长度 > 0
        token = TokenUtil.cutPrefix(token);
        if (null != token && token.length() > 0) {
            return token;
        }

        return null;
    }

    /**
     * 3. 保存到上下文
     * @param authorization 授权主体
     */
    private void setAuthContext (Authorization authorization) {
        AuthContext.setContext(authorization);
    }

    @Override
    public int getOrder() {
        return AuthConstant.Filter.AUTHORIZATION_VALIDATE_FILTER;
    }
}
